#include "helper.h"
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <limits>
#include <cstdio>

using namespace std;

vector<string> split(string str, string pattern)
{
    string::size_type pos;
    vector<string> result;
    str += pattern; //扩展字符串以方便操作
    int size = str.size();

    for (int i = 0; i < size; i++)
    {
        pos = str.find(pattern, i);
        if (pos < (uint)size)
        {
            string s = str.substr(i, pos - i);
            result.push_back(s);
            i = pos + pattern.size() - 1;
        }
    }
    return result;
}

string doubleToString(double value, int precisionAfterPoint)
{
    return to_string(value);
}

void print_zval_type(zval *value)
{
    php_printf("mem addr: %ld, type value:%d\n", reinterpret_cast<long>(value), Z_TYPE_P(value));
    // switch (Z_TYPE_P(value))
    // {
    // case IS_TRUE:
    //     php_printf("print type: bool\n");
    //     break;
    // case IS_FALSE:
    //     php_printf("print type: bool\n");
    //     break;
    // case IS_STRING:
    //     php_printf("print type: string\n");
    //     break;
    // case IS_LONG:
    //     php_printf("print type: long\n");
    //     break;
    // case IS_DOUBLE:
    //     php_printf("print type: double\n");
    //     break;
    // case IS_ARRAY:
    //     php_printf("print type: array\n");
    //     break;
    // case IS_OBJECT:
    //     php_printf("print type: object\n");
    //     break;
    // case IS_RESOURCE:
    //     php_printf("print type: resource\n");
    //     break;
    // case IS_REFERENCE:
    //     php_printf("print type: reference\n");
    //     break;
    // case IS_CALLABLE:
    //     php_printf("print type: callable\n");
    //     break;
    // // case IS_CONSTANT:
    // //     php_printf("print type: constant\n");
    // //     break;
    // case IS_CONSTANT_AST:
    //     php_printf("print type: constant ast\n");
    //     break;
    // case IS_ITERABLE:
    //     php_printf("print type: iterable\n");
    //     break;
    // case IS_VOID:
    //     php_printf("print type: iterable\n");
    //     break;
    // case IS_INDIRECT:
    //     php_printf("print type: indirect\n");
    //     break;
    // case IS_PTR:
    //     php_printf("print type: ptr\n");
    //     break;
    // default:
    //     php_printf("print type: unknown\n");
    //     break;
    // }
}

// zval *push_read_property(zval *obj, const char *property)
// {
//     if (Z_TYPE_P(obj) == IS_OBJECT)
//     {
//         // zend_object *object = obj->value.obj;
//         // zend_class_entry *ce;

//         // ce = object->ce;

//         zend_class_entry *ce = zend_get_class_entry(obj);

//         return zend_read_property(ce, obj, property, strlen(property), 0);
//     }
//     return nullptr;
// }

zval *read_obj_property(zval *obj, const char *property, string obj_class)
{
    if (Z_TYPE_P(obj) == IS_OBJECT)
    {
        // zend_object *object = obj->value.obj;
        // zend_class_entry *ce;

        // ce = object->ce;

        zend_class_entry *ce = zend_get_class_entry(obj);
        string class_name = ce->name;

        char *cn_c = new char[class_name.length() + 1];
        strcpy(cn_c, class_name.c_str());
        php_printf("read_obj_property: object:%s\n", cn_c);

        zval *res = zend_read_property(ce, obj, property, strlen(property), 0);
        if (Z_TYPE_P(res) == IS_OBJECT && class_name == obj_class)
        {
            return res;
        }
    }
    return nullptr;
}

zval *read_property(zval *obj, const char *property)
{
    if (Z_TYPE_P(obj) == IS_OBJECT)
    {
        // zend_object *object = obj->value.obj;
        // zend_class_entry *ce;

        // ce = object->ce;

        zend_class_entry *ce = zend_get_class_entry(obj);

        return zend_read_property(ce, obj, property, strlen(property), 0);
    }
    return nullptr;
}

long func_num_args(zend_execute_data *execute_data)
{
    long num = (long)(zend_uintptr_t) * (execute_data->function_state.arguments);
    return num;
}

zval *func_get_arg(zend_execute_data *execute_data, long pos)
{
    long num = func_num_args(execute_data);
    if (num < pos)
    {
        return nullptr;
    }
    void **p = execute_data->function_state.arguments;
    zval *arg;
    ALLOC_ZVAL(arg);
    *arg = **((zval **)(p - (num - pos + 1)));

    return arg;
}

void print_hashtable_zval(zval *htz)
{
    HashTable *ht = Z_ARRVAL_P(htz);
    HashPosition position;
    zval **data = NULL;

    for (zend_hash_internal_pointer_reset_ex(ht, &position);
         zend_hash_get_current_data_ex(ht, (void **)&data, &position) == SUCCESS;
         zend_hash_move_forward_ex(ht, &position))
    {

        char *key = NULL;
        uint klen;
        ulong index;
        int vtype = Z_TYPE_PP(data);

        if (zend_hash_get_current_key_ex(ht, &key, &klen, &index, 0, &position) == HASH_KEY_IS_STRING)
        {
            php_printf("key:%s => %d\n", key, vtype);
        }
        else
        {
            php_printf("key:%ld => %d\n", index, vtype);
        }
    }
}

string str_replace_all(string source, string old_str, string new_str)
{
    for (string::size_type pos(0); pos != string::npos; pos += new_str.length())
    {
        if ((pos = source.find(old_str, pos)) != string::npos)
            source.replace(pos, old_str.length(), new_str);
        else
            break;
    }
    return source;
}